package com.gorilla.aspect.impl;

import com.alibaba.fastjson.JSON;
import com.gorilla.aspect.annotation.Idempotent;
import com.gorilla.aspect.exception.GorillaException;
import com.gorilla.aspect.util.KeyResolverUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;


@Aspect
@Slf4j
public class IdempotentAspect {


	@Autowired
	private StringRedisTemplate redisTemplate;

	@Pointcut("@annotation(com.gorilla.aspect.annotation.Idempotent)")
	public void annotationPointCut() {
	}

	@SneakyThrows
	@Around("annotationPointCut()")
	public Object aroundCut(ProceedingJoinPoint joinPoint) {


		Object proceed = null;
		String key = null;
		boolean locked = false;
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			Method method = signature.getMethod();
			if (!method.isAnnotationPresent(Idempotent.class)) {
				return joinPoint.proceed();
			}
			Idempotent idempotent = method.getAnnotation(Idempotent.class);


			String value = idempotent.value();
			key = value+":"+ KeyResolverUtil.resolver(idempotent.key(), joinPoint);
			String dateKey = key+"_data";

			long expireTime = idempotent.expireTime();

			TimeUnit timeUnit = idempotent.timeUnit();
			String tem = LocalDateTime.now().toString().replace("T", " ");

			boolean result = redisTemplate.opsForValue().setIfAbsent(key, tem, expireTime, timeUnit);
			if(result){
				locked = true;
				proceed = joinPoint.proceed();

				//是否进行缓存
				if(!StringUtils.isEmpty(idempotent.unless())){//执行成功
					if(!KeyResolverUtil.resolver(idempotent.unless(),proceed)){
						redisTemplate.opsForValue().set(dateKey, JSON.toJSONString(proceed),expireTime, timeUnit);
					}
					return proceed;
				}else{
					redisTemplate.opsForValue().set(dateKey,JSON.toJSONString(proceed),expireTime, timeUnit);
					return proceed;
				}
			}else{
				if(redisTemplate.hasKey(dateKey)){
					String data = redisTemplate.opsForValue().get(dateKey);
					return JSON.parseObject(data,method.getReturnType());
				}else{
					throw new GorillaException(idempotent.info());
				}
			}
		} catch (Exception e){
			log.error("Idempotent2_Exception",e);
			if(locked){
				redisTemplate.delete(key);
			}
			throw e;
		}
	}



}
